/*
 * Copyright 2013 Faisal Aslam.
 * All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * only, as published by the Free Software Foundation.
 * 
 * This code is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 3 for more details (a copy is
 * included in the LICENSE file that accompanied this code).
 * 
 * You should have received a copy of the GNU General Public License
 * version 3 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Faisal Aslam (faisal.aslam AT gmail.com)
 * if you need additional information or have any questions.
 */
package org.parallelCompiler.modifyCF.logic;

import java.util.Vector;
import takatuka.classreader.dataObjs.*;
import takatuka.classreader.dataObjs.attribute.*;
import takatuka.classreader.logic.constants.*;
import takatuka.classreader.logic.factory.FactoryFacade;
import takatuka.classreader.logic.factory.FactoryPlaceholder;
import takatuka.optimizer.cpGlobalization.logic.util.*;

/**
 * Will provide some function.
 * 
 * 1) take argument of library name and add the library in clinit method.
 * 
 * 2) create a native method given its name, arguments and return type.
 * 
 * 3) Add code to call native method.
 * 
 * @author Faisal Aslam
 */
public class NativeMethodUtil {

    private static final String CLINIT = "<clinit>";
    private static final String VOID_DESCRIPTOR = "()V";
    private static final FactoryPlaceholder placeHolder = FactoryPlaceholder.getInstanceOf();
    private static final FactoryFacade facade = placeHolder.getFactory();

    public NativeMethodUtil() {
    }

    static public void loadLibrary(ClassFile cFile, String libName) throws Exception {
        Oracle oracle = Oracle.getInstanceOf();
        MethodInfo clinitMethod = Util.findMethod(cFile, CLINIT);
        CodeAtt codeAtt = clinitMethod.getCodeAtt();
        Vector<Instruction> instrVec = codeAtt.getInstructions();
        if (clinitMethod == null) {
            addMethod(cFile, CLINIT, VOID_DESCRIPTOR, true, false, false, false);
            clinitMethod = Util.findMethod(cFile, CLINIT);
            Instruction returnInst = Util.createInstr(codeAtt, JavaInstructionsOpcodes.RETURN);
            instrVec.add(returnInst);
            codeAtt.setInstructions(instrVec);
        }
        int cpIndex = Util.addStringInfoInCP(cFile.getConstantPool(), libName);
        Instruction LDC = Util.createInstr(codeAtt, JavaInstructionsOpcodes.LDC, facade.createUn(cpIndex).trim(2));
        instrVec.add(0, LDC);
        codeAtt.setInstructions(instrVec);
        int newStackSize = codeAtt.getMaxStack().intValueUnsigned()+1;
        codeAtt.setMaxStack(facade.createUn(newStackSize).trim(2));
        //       createInstr(codeAtt, opCode)

    }

    static public int addNativeMethod(ClassFile cFile, String methodName,
            String methodDescriptor, boolean isStatic) {
        return addMethod(cFile, methodName, methodDescriptor, isStatic, true, false, true);
    }

    static private int addMethod(ClassFile cFile, String methodName,
            String methodDescriptor, boolean isStatic, boolean isNative, boolean isSuper, boolean isPublic) {
        Oracle oracle = Oracle.getInstanceOf();
        Un accessFlag = AccessFlags.createAccessFlagsValue(isStatic, isNative, isSuper, isPublic);
        return oracle.addFieldInfoInClassFileAndCP(methodName, methodDescriptor, cFile,
                true, accessFlag);
    }
}
